package org.ysling.litemall.admin.service;
// Copyright (c) [ysling] [927069313@qq.com]
// [litemall-plus] is licensed under Mulan PSL v2.
// You can use this software according to the terms and conditions of the Mulan PSL v2.
// You may obtain a copy of Mulan PSL v2 at:
//             http://license.coscl.org.cn/MulanPSL2
// THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND,
// EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT,
// MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE.
// See the Mulan PSL v2 for more details.
import com.github.binarywang.wxpay.bean.notify.WxPayNotifyResponse;
import com.github.binarywang.wxpay.bean.result.WxPayRefundResult;
import lombok.extern.slf4j.Slf4j;
import org.ysling.litemall.core.express.service.ExpressService;
import org.ysling.litemall.core.service.ActionLogService;
import org.ysling.litemall.core.service.NotifyCoreService;
import org.ysling.litemall.core.service.OrderCoreService;
import org.ysling.litemall.core.weixin.service.SubscribeMessageService;
import org.ysling.litemall.core.tasks.impl.OrderUnconfirmedTask;
import org.ysling.litemall.core.tasks.service.TaskService;
import org.ysling.litemall.core.utils.JacksonUtil;
import org.ysling.litemall.core.utils.response.ResponseUtil;
import org.ysling.litemall.core.weixin.service.WeixinPayCoreService;
import org.ysling.litemall.db.domain.*;
import org.ysling.litemall.db.service.*;
import org.ysling.litemall.db.constant.OrderConstant;
import org.ysling.litemall.db.vomain.OrderVo;
import org.ysling.litemall.db.vomain.UserVo;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import java.math.BigDecimal;
import java.time.LocalDateTime;
import java.util.*;

@Slf4j
@Service
public class AdminOrderService {

    @Autowired
    private LitemallOrderGoodsService orderGoodsService;
    @Autowired
    private OrderCoreService orderCoreService;
    @Autowired
    private LitemallOrderService orderService;
    @Autowired
    private LitemallGrouponService grouponService;
    @Autowired
    private NotifyCoreService notifyCoreService;
    @Autowired
    private SubscribeMessageService subscribeMessageService;
    @Autowired
    private LitemallUserService userService;
    @Autowired
    private TaskService taskService;
    @Autowired
    private LitemallCommentService commentService;
    @Autowired
    private WeixinPayCoreService payCoreService;
    @Autowired
    private ExpressService expressService;
    @Autowired
    private ActionLogService logHelper;

    public Object list(String mobile, String consignee,
                       String orderSn, LocalDateTime start,
                       LocalDateTime end, List<Short> orderStatusArray,
                       Integer page, Integer limit, String sort, String order) {
        List<LitemallOrder> orderList = orderService.querySelective(mobile, consignee, orderSn, start, end, orderStatusArray, page, limit, sort, order);

        List<OrderVo> data = new ArrayList<>();
        for (LitemallOrder o :orderList) {
            OrderVo orderVo = new OrderVo();
            orderVo.setId(o.getId());
            orderVo.setOrderSn(o.getOrderSn());
            orderVo.setAddTime(o.getAddTime());
            orderVo.setOrderStatus(o.getOrderStatus().intValue());
            orderVo.setOrderPrice(o.getOrderPrice());
            orderVo.setActualPrice(o.getActualPrice());
            orderVo.setConsignee(o.getConsignee());
            orderVo.setAddress(o.getAddress());
            orderVo.setIntegralPrice(o.getIntegralPrice());
            orderVo.setMessage(o.getMessage());
            orderVo.setFreightPrice(o.getFreightPrice());
            orderVo.setMobile(o.getMobile());
            orderVo.setPayTime(o.getPayTime());
            orderVo.setBatchTime(o.getBatchTime());
            orderVo.setShipChannel(expressService.getVendorName(o.getShipChannel()));
            orderVo.setShipSn(o.getShipSn());
            orderVo.setUserId(o.getUserId());
            UserVo userVo = userService.findUserVoById(o.getUserId());
            orderVo.setUserName(userVo.getNickname());
            orderVo.setUserAvatar(userVo.getAvatar());
            orderVo.setGoodsVoList(orderGoodsService.queryByOid(o.getId()));

            data.add(orderVo);
        }
        return ResponseUtil.okList(data , orderList);
    }

    public Object detail(Integer id) {
        LitemallOrder order = orderService.findById(id);
        List<LitemallOrderGoods> orderGoods = orderGoodsService.queryByOid(id);
        UserVo user = userService.findUserVoById(order.getUserId());
        Map<String, Object> data = new HashMap<>();
        data.put("order", order);
        data.put("orderGoods", orderGoods);
        data.put("user", user);

        return ResponseUtil.ok(data);
    }

    /**
     * 订单退款
     * <p>
     * 1. 检测当前订单是否能够退款;
     * 2. 微信退款操作;
     * 3. 设置订单退款确认状态；
     * 4. 订单商品库存回库。
     * <p>
     * TODO
     * 虽然接入了微信退款API，但是从安全角度考虑，建议开发者删除这里微信退款代码，采用以下两步走步骤：
     * 1. 管理员登录微信官方支付平台点击退款操作进行退款
     * 2. 管理员登录litemall管理后台点击退款操作进行订单状态修改和商品库存回库
     *
     * @param body 订单信息，{ orderId：xxx }
     * @return 订单退款操作结果
     */
    public Object refund(String body) {
        Integer orderId = JacksonUtil.parseInteger(body, "orderId");
        String refundMoney = JacksonUtil.parseString(body, "refundMoney");
        if (orderId == null) {
            return ResponseUtil.badArgument();
        }
        if (Objects.isNull(refundMoney)) {
            return ResponseUtil.badArgument();
        }

        LitemallOrder order = orderService.findById(orderId);
        if (order == null) {
            return ResponseUtil.badArgument();
        }

        if (order.getActualPrice().compareTo(new BigDecimal(refundMoney)) != 0) {
            return ResponseUtil.badArgumentValue();
        }

        // 如果订单不是退款状态，则不能退款
        if (!(OrderConstant.isRefundStatus(order) || OrderConstant.isGrouponFailStatus(order))){
            return ResponseUtil.fail("订单不是退款状态，不能退款");
        }

        //如果订单金额为零则跳过退款接口直接修改订单状态
        if (new BigDecimal(refundMoney).compareTo(BigDecimal.ZERO) != 0) {
            // 微信退款
            WxPayRefundResult refundResult = payCoreService.wxPayRefund(order);
            order.setRefundContent(refundResult.getRefundId());
        }else {
            order.setRefundContent(order.getOrderSn());
        }

        // 设置订单取消状态
        LocalDateTime now = LocalDateTime.now();
        order.setRefundTime(now);
        order.setRefundType("微信退款接口");
        order.setRefundAmount(order.getActualPrice());
        order.setOrderStatus(OrderConstant.STATUS_REFUND_CONFIRM);
        if (orderService.updateVersionSelective(order) == 0) {
            throw new RuntimeException("更新数据已失效");
        }

        // 返还订单
        orderCoreService.orderRelease(order);

        //订单退款订阅通知
        LitemallUser user = userService.findById(order.getUserId());
        subscribeMessageService.refundSubscribe(user.getWeixinOpenid(),order);
        
        logHelper.logOrderSucceed("退款", "订单编号 " + order.getOrderSn());
        return ResponseUtil.ok();
    }

    /**
     * 发货
     * 1. 检测当前订单是否能够发货
     * 2. 设置订单发货状态
     *
     * @param body 订单信息，{ orderId：xxx, shipSn: xxx, shipChannel: xxx }
     * @return 订单操作结果
     * 成功则 { errno: 0, errmsg: '成功' }
     * 失败则 { errno: XXX, errmsg: XXX }
     */
    public Object ship(String body) {
        Integer orderId = JacksonUtil.parseInteger(body, "orderId");
        String shipSn = JacksonUtil.parseString(body, "shipSn");
        String shipChannel = JacksonUtil.parseString(body, "shipChannel");
        if (orderId == null || Objects.equals(shipSn,"null") || Objects.equals(shipChannel,"null")) {
            return ResponseUtil.badArgument();
        }

        LitemallOrder order = orderService.findById(orderId);
        if (order == null) {
            return ResponseUtil.badArgument();
        }

        // 如果订单不是已付款状态，则不能发货
        if(!(OrderConstant.isPayStatus(order) || OrderConstant.isBtlPayStatus(order) || OrderConstant.isGrouponSucceedStatus(order))){
            return ResponseUtil.fail("订单不能发货");
        }

        order.setShipSn(shipSn);
        order.setShipChannel(shipChannel);
        order.setShipTime(LocalDateTime.now());
        order.setOrderStatus(OrderConstant.STATUS_SHIP);
        if (orderService.updateVersionSelective(order) == 0) {
            return ResponseUtil.updatedDateExpired();
        }

        //订单发货订阅通知
        LitemallUser user = userService.findById(order.getUserId());
        subscribeMessageService.shipSubscribe(user.getWeixinOpenid(),order);

        //添加确认收货定时任务
        taskService.addTask(new OrderUnconfirmedTask(orderId));

        logHelper.logOrderSucceed("发货", "订单编号 " + order.getOrderSn());
        return ResponseUtil.ok();
    }

    /**
     * 删除订单
     * 1. 检测当前订单是否能够删除
     * 2. 删除订单
     *
     * @param body 订单信息，{ orderId：xxx }
     * @return 订单操作结果
     * 成功则 { errno: 0, errmsg: '成功' }
     * 失败则 { errno: XXX, errmsg: XXX }
     */
    public Object delete(String body) {
        Integer orderId = JacksonUtil.parseInteger(body, "orderId");
        LitemallOrder order = orderService.findById(orderId);
        if (order == null) {
            return ResponseUtil.badArgument();
        }

        // 如果订单不是关闭状态(已取消、系统取消、已退款、用户已确认、系统已确认)，则不能删除
        if (!(OrderConstant.isCancelStatus(order) || OrderConstant.isAutoCancelStatus(order) ||
                OrderConstant.isConfirmStatus(order) || OrderConstant.isAutoConfirmStatus(order) ||
                OrderConstant.isRefundConfirmStatus(order))){
            return ResponseUtil.fail( "订单不能删除");
        }

        // 删除订单
        orderService.deleteById(orderId);
        // 删除订单商品
        orderGoodsService.deleteByOrderId(orderId);
        logHelper.logOrderSucceed("删除", "订单编号 " + order.getOrderSn());
        return ResponseUtil.ok();
    }

    /**
     * 回复订单商品
     *
     * @param body 订单信息，{ orderId：xxx }
     * @return 订单操作结果
     * 成功则 { errno: 0, errmsg: '成功' }
     * 失败则 { errno: XXX, errmsg: XXX }
     */
    public Object reply(String body) {
        Integer commentId = JacksonUtil.parseInteger(body, "commentId");
        if (commentId == null || commentId == 0) {
            return ResponseUtil.badArgument();
        }
        // 目前只支持回复一次
        LitemallComment comment = commentService.findById(commentId);
        if(comment == null){
            return ResponseUtil.badArgument();
        }
        if (!Objects.isNull(comment.getAdminContent())) {
            return ResponseUtil.fail( "订单商品已回复！");
        }
        String content = JacksonUtil.parseString(body, "content");
        if (Objects.isNull(content)) {
            return ResponseUtil.badArgument();
        }
        // 更新评价回复
        comment.setAdminContent(content);
        commentService.updateById(comment);

        return ResponseUtil.ok();
    }

    /**
     * 线下收款 订单信息，{ orderId：xxx , newMoney: xxx}
     * @param body
     * @return
     */
    public Object pay(String body) {
        Integer orderId = JacksonUtil.parseInteger(body, "orderId");
        String newMoney = JacksonUtil.parseString(body, "newMoney");

        if (orderId == null || Objects.isNull(newMoney)) {
            return ResponseUtil.badArgument();
        }

        LitemallOrder order = orderService.findById(orderId);
        if (order == null) {
            return ResponseUtil.badArgument();
        }

        if (!(OrderConstant.isCreateStatus(order) || OrderConstant.isGrouponNoneStatus(order))){
            return ResponseUtil.fail("当前订单状态不支持线下收款");
        }

        //设置订单状态
        BigDecimal actualPrice = new BigDecimal(newMoney);
        order.setActualPrice(actualPrice);
        order.setOrderStatus(OrderConstant.STATUS_BTL_PAY);
        if (orderService.updateVersionSelective(order) == 0) {
            return WxPayNotifyResponse.fail("更新数据已失效");
        }

        //  支付成功，有团购信息，更新团购信息
        LitemallGroupon groupon = grouponService.queryByOrderId(order.getId());
        if (groupon != null) {
            orderCoreService.updateGrouponStatus(groupon);
        }else {
            //给商家发送通知
            notifyCoreService.orderNotify(order);
        }
        return ResponseUtil.ok();
    }
}
